const cons = require('consolidate');
const MetalSmith = require('metalsmith');
const axios = require('axios');
// loading样式
const ora = require('ora');
// 选择模板
const inquirer = require('inquirer');
const { promisify } = require('util');
const path = require('path');
const fs = require('fs');
let downloadGit = require('download-git-repo');

// 将异步的api转换为promise形式
downloadGit = promisify(downloadGit);

// 统一所有的模板引擎
let { render } = require('consolidate').ejs;

render = promisify(render);
let ncp = require('ncp');
ncp = promisify(ncp);

const { downloadDir } = require('./constants');

// create 的功能是创建项目
// 拉取你自己的所有的项目， 让用户来选择安装哪个项目 projectName
// 选择完成后， 显示所有的版本号
// 可能还需要用户配置一些数据来结合渲染项目

// https://api.github.com/orgs/zhu-cli/repos 获取组织下的仓库

const repoAddress = 'https://api.github.com/orgs/zhu-cli';
const tagAddress = 'https://api.github.com/repos/zhu-cli';

// 获取项目列表
const fetchRepoList = async () => {
  const { data } = await axios.get(`${repoAddress}/repos`);
  return data;
};

const fetchTagList = async (repo) => {
  const { data } = await axios.get(`${tagAddress}/${repo}/tags`);
  return data;
};

const download = async (repo, tag) => {
  let api = `zhu-cli/${repo}`;
  if (tag) {
    api += `#${tag}`;
  }
  const dest = `${downloadDir}/${repo}`;
  await downloadGit(api, dest);
  return dest; // 显示下载的最终目录
};

// 封装 loading
const waitFnloading = (fn, message) => async (...args) => {
  const spinner = ora(message);
  spinner.start();
  const result = await fn(...args);
  spinner.succeed();
  return result;
};

module.exports = async (projectName) => {
  // projectName 是 require引入方法，传入的参数
  // 获取仓库数据
  try {
    // 1.获取仓库数据
    let repos = await waitFnloading(fetchRepoList, 'fetch template...')();
    // 筛选数据
    repos = repos.map((v) => v.name);
    console.log(repos);
    // 选择模板 repo就是选择后的模板
    const { repo } = await inquirer.prompt({
      name: 'repo',
      type: 'list',
      message: 'please choise a template to create project',
      choices: repos,
    });
    console.log('选择的模板', repo);
    // 2.获取tag数据
    let tags = await waitFnloading(fetchTagList, 'fetch tags ....')(repo);

    tags = tags.map((t) => t.name);
    // 选择tag
    const { tag } = await inquirer.prompt({
      name: 'tag',
      type: 'list',
      message: 'please choise tags to create project',
      choices: tags,
    });
    console.log('选择的tag', tag);
    // 3.拿到 repo和tag然后开始下载模板
    const result = await waitFnloading(download, 'download template...')(repo, tag);
    console.log('下载的目录', result);

    // 拿到下载目录，直接拷贝到当前执行目录下即可 ncp
    if (!fs.existsSync(path.join(result, 'ask.js'))) {
      // 没有ask文件的，直接拷贝
      await ncp(result, path.resolve(projectName));
    } else {
      // 有ask文件需要用户选择，选择后编译模板
      // metalsmith --编译模板需要这个包
      // 需要渲染模板的接口：https://github.com/zhu-cli/vue-template/blob/master/package.json
      // 1.让用户填写信息
      await new Promise((resolve, reject) => {
        MetalSmith(__dirname)
          .source(result)
          .destination(path.resolve(projectName))
          .use(async (files, metal, done) => {
            console.log(files);
            const args = require(path.join(result, 'ask.js'));
            const obj = await inquirer.prompt(args);
            const meta = metal.metadata();
            Object.assign(meta, obj);
            delete files['ask.js'];
            done();
          })
          .use((files, metal, done) => {
            let obj = metal.metadata();
            Reflect.ownKeys(files).forEach(async (file) => {
              // 是要处理的文件
              if (file.includes('js') || file.includes('json')) {
                let content = files[file].contents.toString();
                if (content.includes('<%')) {
                  content = await render(content, obj);
                  files[file].contents = Buffer.from(content); // 渲染结果
                }
              }
            });
            done();
          })
          .build((err) => {
            if (err) {
              reject();
            } else {
              resolve();
            }
          });
      });
    }
  } catch (error) {
    console.log(error);
  }
};
